package pl.put.to.regression.linearprogramming;

import java.util.ArrayList;
import java.util.List;

import lpsolve.LpSolve;
import lpsolve.LpSolveException;
import pl.put.to.regression.data.DataInstance;
import pl.put.to.regression.data.DataSet;

public class MinMaxProblem {
	private DataSet dataSet;
	private LpSolve solver;
	private List<Double> aVariables;
	private int aVectorStartPosition;

	public MinMaxProblem(DataSet dataSet) {
		this.dataSet = dataSet;
		this.aVariables = new ArrayList<Double>();

		prepareSolver();
	}

	private void prepareSolver() {
		try {
			int numberOfVariables = dataSet.getNumberOfInstances() * 2 /* (alpha+), (alpha-) */+ dataSet.getNumberOfVariables()/* (a) */+ 1 /* (gamma) */;

			// initialize solver
			solver = LpSolve.makeLp(0, numberOfVariables);

			// position of gamma variable in the variables vector
			int gammaPosition = numberOfVariables - 1;
			aVectorStartPosition = dataSet.getNumberOfInstances() * 2;

			ReusableVariablesVector variablesVector = new ReusableVariablesVector(numberOfVariables);

			prepareConstraints(gammaPosition, variablesVector);

			prepareObjectiveFunction(gammaPosition, variablesVector);

		} catch (LpSolveException e) {
			e.printStackTrace();
		}
	}

	private void prepareObjectiveFunction(int gammaPosition, ReusableVariablesVector variablesVector) throws LpSolveException {
		variablesVector.setVariable(gammaPosition, "1");

		// objective function: min z() = gamma
		solver.strSetObjFn(variablesVector.toString());
	}

	private void prepareConstraints(int gammaPosition, ReusableVariablesVector variablesVector) throws LpSolveException {
		// begin iteration over all instances
		int variablesCurrentPosition = 0;

		for (DataInstance instance : dataSet.getInstances()) {
			// prepare expression a^T * x_i
			for (int i = 0; i < dataSet.getNumberOfVariables(); ++i) {
				variablesVector.setVariable(aVectorStartPosition + i, instance.getVariableValues().get(i).toString());
			}

			int alphaPositivePosition = variablesCurrentPosition++;
			int alphaNegativePosition = variablesCurrentPosition++;

			// prepare expression (alpha+)_i - (alpha-)_i
			variablesVector.setVariable(alphaPositivePosition, "1");
			variablesVector.setVariable(alphaNegativePosition, "-1");

			// constraint a^T * x_i + (alpha+)_i - (alpha-)_i = y_i
			solver.strAddConstraint(variablesVector.toString(), LpSolve.EQ, instance.getClassVariableValue());

			variablesVector.clearVector();

			variablesVector.setVariable(gammaPosition, "1");
			variablesVector.setVariable(alphaPositivePosition, "-1");
			variablesVector.setVariable(alphaNegativePosition, "-1");

			// constraint gamma - (alpha+)_i - (alpha-)_i >= 0
			solver.strAddConstraint(variablesVector.toString(), LpSolve.GE, 0);

			variablesVector.clearVector();

			// constraint (alpha+)_i >= 0
			variablesVector.setVariable(alphaPositivePosition, "1");
			solver.strAddConstraint(variablesVector.toString(), LpSolve.GE, 0);
			variablesVector.clearVector();

			// constraint (alpha-)_i >= 0
			variablesVector.setVariable(alphaNegativePosition, "1");
			solver.strAddConstraint(variablesVector.toString(), LpSolve.GE, 0);
			variablesVector.clearVector();
		}
	}

	public void solve() {
		try {
			solver.solve();

			double[] allVariables = solver.getPtrVariables();

			for (int i = aVectorStartPosition; i < aVectorStartPosition + dataSet.getNumberOfVariables(); ++i) {
				aVariables.add(allVariables[i]);
			}

		} catch (LpSolveException e) {
			e.printStackTrace();
		}
	}

	public void disposeProblem() {
		solver.deleteLp();
	}

	public Double getObjective() {
		Double result = null;

		try {
			result = solver.getObjective();
		} catch (LpSolveException e) {
			e.printStackTrace();
		}

		return result;
	}

	public List<Double> getACoefficients() {
		return aVariables;
	}
}
